<?php

function apply_patch($patch)
{
	$config_php = $patch->site_dir.'etc/conf.d/db.php';
	include ($config_php);
	require_once 'UpMysql.php';
	$db = new UpMysql($config);
	if (!$db->init()) {
		$patch->error = $db->error;
		return false;
	}
	$patch->db = $db;
	$status = true
		&& fix_tdi_show_in_menu_item($patch)
		&& update_db_schema($patch)
		&& update_config_php($patch)
		&& update_db_class_member($patch)
		&& update_db_fix_root_tdi_access($patch)
		;
	return $status;
}

// 1.1.1
function fix_tdi_show_in_menu_item($patch)
{
	// FIX : 2008-04-24 : lucky
	//
	// баг введен апдейтером v1.1rc при обновление структур ftdi
	//
	// ошибочно присваивалось значение $data['config'] = serialize(array('show_in_main_menu' => true));
	// на самом деле нужно $data['show_in_main_menu'] = true;
	//
	assert('$patch->db');

	$db = $patch->db;
	$prefix = $db->db_prefix;

	// update ftdi objects
	// .show_in_main_menu = true
	$result = $db->query("SELECT * FROM {$prefix}objects WHERE type='ftdi'");
	if ($result) {
		while ($row = $db->getOne($result)) {
			$oid = $row['oid'];
			$data = unserialize($row['data']);

			if (isset($data['config'])) {
				$data_config = unserialize($data['config']);
				unset($data_config['show_in_main_menu']);
				if (count($data_config) == 0) {
					unset($data['config']);
				} else { 
					$data['config'] == serialize($data_config); 
				}
			}

			if (!isset($data['show_in_main_menu'])) {
				// если еще не успели установить в админке
				$data['show_in_main_menu'] = true;
			}
			$data_serialised = $db->escape(serialize($data));
			$update = $db->query("UPDATE {$prefix}objects SET data='{$data_serialised}' WHERE oid={$oid}");
			if (!$update) {
				$patch->error = "Не смог обновить конфигурацию разделов, SQL error: " . $db->error;
				return false;   
			}

		}

	} else {
		$patch->error = "Не смог обновить конфигурацию сообществ, SQL error: " . $db->error;
		return false;                   
	}
	return true;
}

// 1.2
/**
 * update_db_schema обновление схемы DB.
 * 
 * @param mixed $patch 
 * @access public
 * @return boolean
 */
function update_db_schema ($patch)
{
	require_once 'MysqlImport.php';
	$patch_sql = $patch->patch_dir . 'patch.sql';
	$sql = file_get_contents($patch_sql);

	$prefix = $patch->db->db_prefix;
	$sql = str_replace('{{prefix}}', $prefix, $sql);

	$m = new MysqlImport(array('import_text' => $sql, 'db' => $patch->db));
	$status = $m->process();
	if (!$status) {
		$patch->error = $m->error;
	}
	return $status;
} // end of function update_db_schema

/**
 * Обновление etc/config.php 
 * 
 * @param mixed $patch 
 * @access public
 * @return boolean
 */
function update_config_php($patch)
{
	// etc/config.php
	//
	// пароль админа в конфиге больше не хранится в открытом виде, а хешируется md5()
	//
	// * admin_password -- пароль администартора узла (@see http://platonica.ru/trac/ticket/448)
	//                     md5(password . project_key)

	$config_php = $patch->site_dir.'etc/config.php';
	include ($config_php);
	require_once 'ConfigPhp.php';
	copy ($patch->site_dir.'etc/config.php.sample', $config_php);
	$cfg = new ConfigPhp(array('source' => $config_php));
	foreach ($self as $k=>$v) {
		if ($k === 'admin_password') {
			$cfg->setParam('admin_password', md5($v . $self['project_key']));
		} else {
			$cfg->setParam($k, $v);
		}
	}

	return true;
}

/**
 * update_db_class_member заполняем новую таблицу class_member
 * 
 * @param mixed $patch 
 * @access public
 * @return void
 */
function update_db_class_member($patch)
{
	$db = $patch->db;
	$prefix = $db->db_prefix;

	// из t_subject_relations найдем отношения юзеров к сообществам.
	// дерево выглядит как-то так
	//
	// - сообщество
	// -- роль
	// --- пользователь
	//
	// тут неплохо бы проверить роль. между сообществом и юзером,
	// ведь он м.б. анонимусом явно. надеюсь таких нет.. =)
	$sql = "
		SELECT O1.oid as community_oid, O2.oid as user_oid
		FROM (
		   SELECT O.oid, t.lft, t.rgt 
		   FROM t_subject_relations t , {$prefix}objects O
			 WHERE t.id_object = O.oid AND O.type = 'fcommunity'
		 ) AS O1, t_subject_relations t2 , {$prefix}objects O2
		WHERE t2.id_object = O2.oid AND O2.type = 'fuser'
			 AND t2.lft > O1.lft AND t2.rgt < O1.rgt
		ORDER BY community_oid, user_oid
	";

	$result = $db->query($sql);
	while ($row = $db->getOne($result)) {
		$community_oid = $row['community_oid'];
		$user_oid = $row['user_oid'];

		// поищем эту пару в множестве class_member
		// если нет, создадим запись
		$select_member_sql = "SELECT oid FROM {$prefix}class_member WHERE user_oid = {$user_oid} AND community_oid = {$community_oid}";
		if (!$db->getOne($db->query($select_member_sql))) {
			// мы не знаем дату регистрации пользователя в сообществе.
			// возьмем дату создания сообщества и дату регистрации пользователя на узле.
			// примем которая позже.
			$_user      = $db->getOne($db->query("SELECT ctime FROM {$prefix}objects WHERE oid = {$user_oid}"));
			$_community = $db->getOne($db->query("SELECT ctime FROM {$prefix}objects WHERE oid = {$community_oid}"));
			$join_date = max(strtotime($_user['ctime']), strtotime($_community['ctime']));

			// создадим структуру членства в сообществе
			$member = array(
						 'user_oid' => $user_oid,
				'community_oid' => $community_oid,
						'join_date' => date('Y-m-d H:i:s', $join_date),
			);

			// сохраним объект для структуры $member
			$fmember = array(
							'type' => 'fmember',
							'data' => serialize($member),
						 'ctime' => date('Y-m-d H:i:s', $date),
						 'mtime' => date('Y-m-d H:i:s', $date),
				'is_deleted' => 0,
			);
			$member['oid'] = $db->insert("{$prefix}objects", $fmember);

			// классифицируем
			$cmember = array(
									'oid' => $member['oid'],
						 'user_oid' => $member['user_oid'],
				'community_oid' => $member['community_oid'],
				    'join_date' => $member['join_date'],
			);
			$db->insert("{$prefix}class_member", $cmember);
		}
	}
	return true;
}

/**
 * update_db_fix_root_tdi_access исправляет права для корня сообщества.
 *
 * Появилас возможность создавать записи, не относящиеся ни к одному разделу сообщества
 * (постить в корень). Нужно выдать права.
 * 
 * @param mixed $patch 
 * @access public
 * @return void
 */
function update_db_fix_root_tdi_access ($patch)
{
	// вот такие права
	// 1 -- разрешить
	// 2 -- запретить
	$acl = array(
		'moderator' => array(
			'item_create'    => 1,
			'item_read'      => 1,
			'comment_create' => 1,
		),
		'member' => array(
			'item_create'    => 1,
			'item_read'      => 1,
			'comment_create' => 1,
		),
		'registered' => array(
			'item_create'    => 2,
			'item_read'      => 1,
			'comment_create' => 1,
		),
		'anonymous' => array(
			'item_create'    => 2,
			'item_read'      => 1,
			'comment_create' => 2,
		),
	);

	// пытаемся эмулировать поведение lib/Model/AclAssistantLowLevel _setAcl

	$db = $patch->db;
	$prefix = $db->db_prefix;

	// для каждого сообщества
	//
	$_c_rs = $db->query("SELECT O.oid FROM {$prefix}objects O INNER JOIN {$prefix}class_community C ON O.oid = C.oid");
	while ($c_row = $db->getOne($_c_rs)) {
		$community_oid = $c_row['oid'];

		// найдем ветку ролей данного сообщества в дереве t_subject_relations
		$select_roles_subject = "SELECT l2.* FROM t_subject_relations as l1 
				LEFT OUTER JOIN t_subject_relations l2 ON l2.parent_id = l1.id 
				WHERE l1.id_object='{$community_oid}' AND l2.id_object='0'
		";
		$roles_subject = $db->getOne($db->query($select_roles_subject));
		$roles_subject_id = $roles_subject['id'];

		// найдем роли в ветке ролей сообщества
		$select_roles_sql = "
			 SELECT l1.*, O.data FROM t_subject_relations
			 LEFT OUTER JOIN t_subject_relations l1 ON l1.parent_id = t_subject_relations.id
			 INNER JOIN {$prefix}objects O ON l1.id_object = O.oid
			 WHERE t_subject_relations.id = {$roles_subject_id} AND l1.id_object<>0
			";
		$_r_rs = $db->query($select_roles_sql);
		while ($r_row = $db->getOne($_r_rs)) {
			// для каждой роли, в t_acl_list изменим записи с соответствующими действий
			// _setAcl использует алгоритм "удалить старое, и вставить новое".
			// поступим так же.
			$role = unserialize($r_row['data']); // информация из объекта роли
			//$role_subject_id = $r_row['id']; // id роли в дереве отношений субъектов
			$role_subject_id = $r_row['id_object']; // id роли в дереве отношений субъектов
			// FIXME : lucky
			// совместно с id_acl_tree нужно использовать id записи в дереве отношений объектов ???
			//
			// по логам похоже, что управлялка правами "платоники" использует $community_oid
			$community_object_id = $community_oid;
			$role_name = $role['name'];
			if (isset($acl[$role_name])) { // если про роль что-то сказано в $acl ...
				$ac = $acl[$role_name];
				// обновим t_acl_list.
				// идентифицируем старые права по тройке (id_acl_tree, id_subject, action)
				$delete_where = array();
				foreach ($ac as $aname => $aopt) {
					$delete_where[] = "(id_acl_tree = {$community_object_id} AND id_subject = {$role_subject_id} AND action = '".$db->escape($aname)."')";
				}
				$delete_where_sql = implode (' OR ', $delete_where);
				// удалим старые права
				$delete_sql = "DELETE FROM t_acl_list WHERE $delete_where_sql";
				$db->query($delete_sql);

				// вставим новые
				foreach ($ac as $aname => $aopt) {
					$t = array(
										 'id' => NULL,
						 'id_subject' => $role_subject_id,
								'comment' => 'New item',
									 'type' => '',
								 'action' => $aname,
										'opt' => $aopt,
						'id_acl_tree' => $community_object_id,
					);
					$db->insert('t_acl_list', $t);
				}
			}
		}
	}

	return true;
} // end of function update_db_fix_root_tdi_access
